<<BACK

The reproducible workflow

My unofficial mantra (again):

Adam Savage via Pintrest

“Adam Savage via Pintrest”

In Data organization with spreadsheets we learned how to write down our data, avoid common errors, and how to save your data so that you and others can read and understand it later.

Now let’s learn how to write down ALL the steps needed to take you from your raw data to publication quality figures. That’s the key to reproducibility, if every step is written down, then others can reproduce your findings! Below, You will learn how to clean your data in a reproducible way using OpenRefine and R.

Pro-tip

After spending all that time entering your raw data, NEVER change it again.

  • Do not edit the data
  • Do not edit the column headers
  • Do not remove ‘outliers’
  • Do not do calculations directly on the raw data

Store your data in a raw_data directory (folder) in your project directory, and never save/write over it!

I advise you archive your data immediately upon collection to reduce risk of data loss. Zenodo and Figshare both offer different free options for archiving your raw data permanently and have some awesome options for embargoes, restricted access, or even private storage to suite your privacy needs. More detail can be found in the Data Archiving & version controls lesson.

By now I’ve probably said the words reproducible and reproducibility so often that it’s starting to lose meaning. Trust me, this is important! (or don’t trust me and read “A manifesto for reproducible Science”). By not ‘hiding’ you workflow, you help:

  • yourself in the future since:
    • you can easily re-run your analysis after ‘reviewer 3’ makes you add one data point
    • you will be able to easily adapt your method
    • you will receive higher citations since someone may cite you for your methods/data
    • you will appear like an honest/better scientist
  • other scientists since:
    • they will be able to reuse/adapt your methods/data
    • they will be better able to judge your methods as appropriate (for your data during review, or their data in the future)
  • ‘Science’ since
    • discoveries are published more efficiently
    • findings are ‘correct’ more often
    • there is increased trust in ‘Science’, which we badly need right now

OpenRefine

OpenRefine (formerly Google Refine) is a powerful tool for working with messy data: cleaning it; transforming it from one format into another. It’s effectively a reproducible way to work in a spreadsheet, no coding is required on your part since it generates a script that details what you did to the data, step by step.

First, let’s open OpenRefine. You’ll notice it opens in the browser, but it’s running locally (does not require internet connection).

The first step is to load your data and to create a project. If you haven’t done so already, please download the zip file the entire github project repository (contains data files, and all the R scripts used to make this very website!) and extract it somewhere convenient.

In OpenRefine, click Choose Files and find the larval abundance.csv which is in the rawdata directory of the project folder. Then click on the Create Project button (you may want to rename the project).

Data Cleaning

You are now working on a copy of the raw data and changes you make in OpenRefine will not ‘break’ your original raw data. In here you can do all the regular ‘spreadsheet-y’ things. You can edit specific cells, sort, undo/redo, view subsets of your data (facet), etc. But the more powerful functions of OpenRefine are:

  • Cluster (click on column header arrow, then Edit cells > Cluster and Edit...) which means “finding groups of different values that might be alternative representations of the same thing”. For example, the two strings “New York” and “new york” are very likely to refer to the same concept and just have capitalization differences.

  • Whitespace management (click on column header arrow, then Edit cells > Common transforms > Trim leading and trailing whitespace. and Edit cells > Common transforms > Collapse consecutive whitespace.) Strings with spaces at the beginning or end are particularly hard for we humans to tell from strings without, but the blank characters will make a difference to the computer. We usually want to remove these.

Reproducibility

OpenRefine saves every change, every edit you make to the dataset in a file you can save on your machine. If you had 20 files to clean, and they all had the same type of errors, and all files had the same columns, you could save the script, open a new file to clean, paste in the script and run it. Voila, clean data.

  • In the Undo / Redo section, click Extract, save the bits desired using the check boxes.
  • Copy the code and paste it into a text editor. Save it as a .txt file.
  • To run these steps on a new dataset, import the new dataset into OpenRefine, open the Extract / Apply section, paste in the .txt file, click Apply.

For more information and tutorials on OpenRefine, please see Data Carpentry

R and RStudio

You can do everything mentioned above in R, it may at first appear more difficult to do it in R, but in my opinion, you will save time by streamlining your workflow using just one tool. I’m not at all disouraging the use of OpenRefine,it is open source and reproducible, such much kudos is due.

For this and future lessons, we will focus on achieving reproducibility by using R which is an open source language and environment for statistical computing and graphics. There are many other such languages (e.g. Python, Julia, MATLAB,etc) used by conservationists, biologist, and oceanographers; however, we believe R is currently the most widely adopted among our colleagues and also has the most convenient set of statistical tools developed for our field.

Intro to R

In “the olden days” we had to walk to school uphill both ways used R in the terminal or using the built in graphical use interface (GUI). Yes, before 2011, RStudio did not exist and yes, R and RStudio are not the same thing!

R is accessible in the terminal (that thing that looks like DOS, and in case my ‘old’ is showing, the thing that is usually a black screen, a blinking cursor and you can only type in commands) by typing R.exe on Windows, or just R on Mac or Linux. In this way, you can type commands in one by one, or similar to what we just saw with OpenRefine, you save your steps/instuctions/commands in a plain text file (with a .R extension instead of .txt) and you can run those in the terminal by typing Rscript.exe scriptname.R on Windows, or just Rscript scriptname.R on Mac or Linux. While I don’t often work in this way anymore, but this is the only option when using Compute Canada’s awesome resources. Using the commands above and a little server specific magic, you can run your scripts on 100’s of processors instead of the one lonely processor on your computer! I’ve used hundreds of years of computer time in a matter of weeks, all for free! If you are affiliated with any Canadian university, you can do this too!

This is what plain vanilla R looks like

“This is what ‘plain vanilla’ R looks like”

R also come with it’s own GUI, in which you can have a script editor, which is essentially a plain text editor, to write/develop your script and an interactive R console where you can actually execute commands. The advantage of the GUI is that you can execute the entire script (‘source’) or run it line by line all while recording your commands in the script file.

Intro to RStudio

RStudio takes this GUI concept a bit further and provides you with several extra support window. If the idea of having windows for your environment, your files, your plots, as well as packages and a help tab all at hand does not excite you, hold on tight, you’ll get there.

There’s also a lot more information about RStudio on their cheatsheet. On the subject of cheatsheets, RStudio has developed several super useful cheatsheets; seriously, you probably will want to print most of these and put them on the wall in your office.

Enough talk! Let’s get coding! The Fundamentals

You read my mind! However, before we get to cleaning the data, we need to cover a few R fundamentals so that what we do in later steps makes sense.

Go back to the project folder you downloaded during the OpenRefine lesson and open the 2017-CHONe-Data.Rproj file. This is an R project file that allows you to set a number of options for the project (see here), but for our purposes just know that the project file is setting the ‘working directory’, it tells R where all your files are. We’ll get back to that later.

In R you can do math, type the command below in your R console and hit enter:

1+1
[1] 2

You can also assign values to variables, or in R parlance an ‘object’ using the <- symbol (shortkey Alt + -).

a <- 2
b <- 1+2

You’ll notice that there was no output this time there was no output. That’s because the value on the right side of the <- symbol is assigned to a object, it goes to your environment (top right window) instead of being output to the console. In the 1+1 example above, there was no object to go to, so it defaulted to printing in the console.

You can see the contents of a object in the environment window, or by typing the object into the console. You can also use these objects like algebra

a
[1] 2
b
[1] 3
a/b
[1] 0.6666667

Up to now, we’ve been dealing with numbers, but R can also deal with character string if surrounded by single or double quotation marks. According to R help (I learned this today!): “Single and double quotes delimit character constants. They can be used interchangeably but double quotes are preferred (and character constants are printed using double quotes), so single quotes are normally only used to delimit character constants containing double quotes.”

A object can also contain multiple values. The : symbol essentially means ‘to’

Another way to do that, with more flexibility is using the c(); the c is short for concatenate and the round brackets indicate that it’s a function. So this concatenate function will concatenate all the ‘arguments’ (things inside the round brackets) which are separated by commas. You can also combine these strategies

There are many functions, but they all follow the format functionName(argument1,argument2,argument3,...) where the ‘arguments’ are the input to the function. Some are fairly straightforward:

mean(y)
[1] 4

But even then there are some surprises, let’s look at the help file for mean(). To do that you can: - if you are on the active line in the console or anywhere in a script, put your cursor on the function and press F1 - in the console, type ?mean (or ??mean if your not so sure mean is the name of the function) - find the help window (one of the tabs for the bottom right window) and use the search bar - also, when all else fails, Google is your friend!

Any method should get you to something like this: In R in most cases you could use = instead of the <- symbol with no problems when you are assigning a value to a object. However, it is best practice to use <- when assigning environment objects and = when defining function arguments. Oh, and NA in R means ‘Not Available’ / Missing Values. Like so:

mean(x, na.rm = T)
[1] 11.72727

I also snuck a TRUE in there; TRUE and FALSE are called logical and are distinct from numeric or character strings. They are sometimes used as arguments values, but they can also used to test things. The == asks if both sides are equal (since the single = is already used for other things), and the != asks if both sides are not equal.

2==1
[1] FALSE
2==2
[1] TRUE
2!=1
[1] TRUE

Creating your own scripts

Up until now, we’ve been playing in the console which means the ‘instructions’ we need to save to reproduce our science are lost (well not really, they can be retrieved from the History tab in the top right, or the console if it hasn’t rolled off the screen). It is a good idea to develop your analysis using a script file (those simple text files with the .R extension I was talking about earlier) because you can save your code easily.

To create a new script, your can click on the little paper with the plus symbol (see below), or you can hit Ctrl+Shift+N (Windows), or Command+Shift+N (Mac), and if that’s not enough options, you can click File > New File > New Script

These scripts are designed to read by R from top to bottom when you hit the button, or Ctrl+Shift+S (Windows), or Command+Shift+S (Mac). Alternatively, you can run portions of your code with Ctrl+Enter (Windows), or Command+Enter (Mac) and either putting your cursor on a line to run the entire line, or highlighting a subsection of code to run just that portion.

Pro-tip If you don’t want R to read something, us the #. Anything that is preceded by a # is regarded as a ‘comment’ by R and it does not try to execute those lines (i.e. R ignores anything after a #). This is also useful if you want to avoid running a few lines of code when you are developing your script. Instead of typing a # in front of each line of code, you can highlight the lines you want commented out and hit Ctrl+Shift+C (Windows), or Command+Shift+C (Mac). Magic!

Commenting is super useful to include human readable instructions/documentation in your code. Let’s give this a try, write this chunk of code into your script, then run it line by line.

x <- 1

x <- 2

# x <- 3

What is the value of x after running all the lines and why?

Indexing and dimensions

Making your own functions

What are ‘packages’?

Reading in data

Homework

install all the packages

<<BACK

LS0tDQp0aXRsZTogIkRhdGEgY2xlYW5pbmcgYW5kIHJhdyBkYXRhIG1hbmFnZW1lbnQiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCi0tLQ0KWzw8QkFDS10oaHR0cHM6Ly9yZW1pLWRhaWdsZS5naXRodWIuaW8vMjAxNy1DSE9OZS1EYXRhLykNCg0KIyBUaGUgcmVwcm9kdWNpYmxlIHdvcmtmbG93DQoNCk15IHVub2ZmaWNpYWwgbWFudHJhIChhZ2Fpbik6DQoNCiFbIkFkYW0gU2F2YWdlIHZpYSBQaW50cmVzdCJdKGh0dHBzOi8vcy1tZWRpYS1jYWNoZS1hazAucGluaW1nLmNvbS81NjR4L2U5L2YyLzE5L2U5ZjIxOWRjZTMwZjEzNjcwMTU4ODMyMzEwYjBlNDJjLmpwZykNCg0KSW4gIFtEYXRhIG9yZ2FuaXphdGlvbiB3aXRoIHNwcmVhZHNoZWV0c10oaHR0cHM6Ly9yZW1pLWRhaWdsZS5naXRodWIuaW8vMjAxNy1DSE9OZS1EYXRhL29yZ2FuaXphdGlvbi5uYi5odG1sKSB3ZSBsZWFybmVkIGhvdyB0byB3cml0ZSBkb3duIG91ciBkYXRhLCBhdm9pZCBjb21tb24gZXJyb3JzLCBhbmQgaG93IHRvIHNhdmUgeW91ciBkYXRhIHNvIHRoYXQgeW91IGFuZCBvdGhlcnMgY2FuIHJlYWQgYW5kIHVuZGVyc3RhbmQgaXQgbGF0ZXIuIA0KDQpOb3cgbGV0J3MgbGVhcm4gaG93IHRvIHdyaXRlIGRvd24gKipBTEwqKiB0aGUgc3RlcHMgbmVlZGVkIHRvIHRha2UgeW91IGZyb20geW91ciByYXcgZGF0YSB0byBwdWJsaWNhdGlvbiBxdWFsaXR5IGZpZ3VyZXMuIFRoYXQncyB0aGUga2V5IHRvIHJlcHJvZHVjaWJpbGl0eSwgaWYgZXZlcnkgc3RlcCBpcyB3cml0dGVuIGRvd24sIHRoZW4gb3RoZXJzIGNhbiByZXByb2R1Y2UgeW91ciBmaW5kaW5ncyEgQmVsb3csIFlvdSB3aWxsIGxlYXJuIGhvdyB0byBjbGVhbiB5b3VyIGRhdGEgaW4gYSByZXByb2R1Y2libGUgd2F5IHVzaW5nIE9wZW5SZWZpbmUgYW5kIFIuDQoNCj4gKipQcm8tdGlwKioNCj4NCj4gQWZ0ZXIgc3BlbmRpbmcgYWxsIHRoYXQgdGltZSBlbnRlcmluZyB5b3VyIHJhdyBkYXRhLCAqKk5FVkVSKiogY2hhbmdlIGl0IGFnYWluLg0KPg0KPiAtIERvIG5vdCBlZGl0IHRoZSBkYXRhDQo+IC0gRG8gbm90IGVkaXQgdGhlIGNvbHVtbiBoZWFkZXJzDQo+IC0gRG8gbm90IHJlbW92ZSAnb3V0bGllcnMnDQo+IC0gRG8gbm90IGRvIGNhbGN1bGF0aW9ucyBkaXJlY3RseSBvbiB0aGUgcmF3IGRhdGENCj4NCj4gU3RvcmUgeW91ciBkYXRhIGluIGEgYHJhd19kYXRhYCBkaXJlY3RvcnkgKGZvbGRlcikgaW4geW91ciBwcm9qZWN0IGRpcmVjdG9yeSwgYW5kIG5ldmVyIHNhdmUvd3JpdGUgb3ZlciBpdCENCg0KSSBhZHZpc2UgeW91IGFyY2hpdmUgeW91ciBkYXRhIGltbWVkaWF0ZWx5IHVwb24gY29sbGVjdGlvbiB0byByZWR1Y2UgcmlzayBvZiBkYXRhIGxvc3MuIFtaZW5vZG9dKGh0dHBzOi8vemVub2RvLm9yZy8pIGFuZCBbRmlnc2hhcmVdKGh0dHBzOi8vZmlnc2hhcmUuY29tLykgYm90aCBvZmZlciBkaWZmZXJlbnQgKipmcmVlKiogb3B0aW9ucyBmb3IgYXJjaGl2aW5nIHlvdXIgcmF3IGRhdGEgKipwZXJtYW5lbnRseSoqIGFuZCBoYXZlIHNvbWUgYXdlc29tZSBvcHRpb25zIGZvciBlbWJhcmdvZXMsIHJlc3RyaWN0ZWQgYWNjZXNzLCBvciBldmVuIHByaXZhdGUgc3RvcmFnZSB0byBzdWl0ZSB5b3VyIHByaXZhY3kgbmVlZHMuIE1vcmUgZGV0YWlsIGNhbiBiZSBmb3VuZCBpbiB0aGUgW0RhdGEgQXJjaGl2aW5nICYgdmVyc2lvbiBjb250cm9sc10oaHR0cHM6Ly9yZW1pLWRhaWdsZS5naXRodWIuaW8vMjAxNy1DSE9OZS1EYXRhL3ZlcnNpb25jb250cm9sLm5iLmh0bWwpIGxlc3Nvbi4NCg0KIVtbXShodHRwczovL3R3aXR0ZXIuY29tL1RyZXZvckFCcmFuY2gvc3RhdHVzLzQ2Njc4MDgyNzk4NTAwMjQ5NildKHBpY3R1cmVzL0JyYW5jaC5wbmcpDQoNCkJ5IG5vdyBJJ3ZlIHByb2JhYmx5IHNhaWQgdGhlIHdvcmRzIHJlcHJvZHVjaWJsZSBhbmQgcmVwcm9kdWNpYmlsaXR5IHNvIG9mdGVuIHRoYXQgaXQncyBzdGFydGluZyB0byBsb3NlIG1lYW5pbmcuIFRydXN0IG1lLCB0aGlzIGlzIGltcG9ydGFudCEgKG9yIGRvbid0IHRydXN0IG1lIGFuZCByZWFkIFsiQSBtYW5pZmVzdG8gZm9yIHJlcHJvZHVjaWJsZSBTY2llbmNlIl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1NjItMDE2LTAwMjEpKS4gQnkgbm90ICdoaWRpbmcnIHlvdSB3b3JrZmxvdywgeW91IGhlbHA6DQoNCi0gKip5b3Vyc2VsZioqIGluIHRoZSBmdXR1cmUgc2luY2U6DQogICAgLSB5b3UgY2FuIGVhc2lseSByZS1ydW4geW91ciBhbmFseXNpcyBhZnRlciAncmV2aWV3ZXIgMycgbWFrZXMgeW91IGFkZCBvbmUgZGF0YSBwb2ludA0KICAgIC0geW91IHdpbGwgYmUgYWJsZSB0byBlYXNpbHkgYWRhcHQgeW91ciBtZXRob2QNCiAgICAtIHlvdSB3aWxsIHJlY2VpdmUgaGlnaGVyIGNpdGF0aW9ucyBzaW5jZSBzb21lb25lIG1heSBjaXRlIHlvdSBmb3IgeW91ciBtZXRob2RzL2RhdGENCiAgICAtIHlvdSB3aWxsIGFwcGVhciBsaWtlIGFuIGhvbmVzdC9iZXR0ZXIgc2NpZW50aXN0IA0KLSAqKm90aGVyIHNjaWVudGlzdHMqKiBzaW5jZToNCiAgICAtIHRoZXkgd2lsbCBiZSBhYmxlIHRvIHJldXNlL2FkYXB0IHlvdXIgbWV0aG9kcy9kYXRhDQogICAgLSB0aGV5IHdpbGwgYmUgYmV0dGVyIGFibGUgdG8ganVkZ2UgeW91ciBtZXRob2RzIGFzIGFwcHJvcHJpYXRlIChmb3IgeW91ciBkYXRhIGR1cmluZyByZXZpZXcsIG9yIHRoZWlyIGRhdGEgaW4gdGhlIGZ1dHVyZSkNCi0gKionU2NpZW5jZScqKiBzaW5jZQ0KICAgIC0gZGlzY292ZXJpZXMgYXJlIHB1Ymxpc2hlZCBtb3JlIGVmZmljaWVudGx5DQogICAgLSBmaW5kaW5ncyBhcmUgJ2NvcnJlY3QnIG1vcmUgb2Z0ZW4NCiAgICAtIHRoZXJlIGlzIGluY3JlYXNlZCB0cnVzdCBpbiAnU2NpZW5jZScsIHdoaWNoIHdlIGJhZGx5IG5lZWQgcmlnaHQgbm93DQogICAgDQogICAgDQojIE9wZW5SZWZpbmUNCg0KT3BlblJlZmluZSAoZm9ybWVybHkgR29vZ2xlIFJlZmluZSkgaXMgYSBwb3dlcmZ1bCB0b29sIGZvciB3b3JraW5nIHdpdGggbWVzc3kgZGF0YTogY2xlYW5pbmcgaXQ7IHRyYW5zZm9ybWluZyBpdCBmcm9tIG9uZSBmb3JtYXQgaW50byBhbm90aGVyLiBJdCdzIGVmZmVjdGl2ZWx5IGEgcmVwcm9kdWNpYmxlIHdheSB0byB3b3JrIGluIGEgc3ByZWFkc2hlZXQsIG5vIGNvZGluZyBpcyByZXF1aXJlZCBvbiB5b3VyIHBhcnQgc2luY2UgaXQgZ2VuZXJhdGVzIGEgc2NyaXB0IHRoYXQgZGV0YWlscyB3aGF0IHlvdSBkaWQgdG8gdGhlIGRhdGEsIHN0ZXAgYnkgc3RlcC4NCg0KRmlyc3QsIGxldCdzIG9wZW4gT3BlblJlZmluZS4gWW91J2xsIG5vdGljZSBpdCBvcGVucyBpbiB0aGUgYnJvd3NlciwgYnV0IGl0J3MgcnVubmluZyBsb2NhbGx5IChkb2VzIG5vdCByZXF1aXJlIGludGVybmV0IGNvbm5lY3Rpb24pLg0KIVtdKHBpY3R1cmVzL09SLnBuZykNCg0KVGhlIGZpcnN0IHN0ZXAgaXMgdG8gbG9hZCB5b3VyIGRhdGEgYW5kIHRvIGNyZWF0ZSBhIHByb2plY3QuIElmIHlvdSBoYXZlbid0IGRvbmUgc28gYWxyZWFkeSwgcGxlYXNlIGRvd25sb2FkIHRoZSBbemlwIGZpbGVdKGh0dHBzOi8vZ2l0aHViLmNvbS9yZW1pLWRhaWdsZS8yMDE3LUNIT05lLURhdGEvYXJjaGl2ZS9naC1wYWdlcy56aXApIHRoZSBlbnRpcmUgIGdpdGh1YiBbcHJvamVjdCByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vcmVtaS1kYWlnbGUvMjAxNy1DSE9OZS1EYXRhLykgKGNvbnRhaW5zIGRhdGEgZmlsZXMsIGFuZCBhbGwgdGhlIFIgc2NyaXB0cyB1c2VkIHRvIG1ha2UgdGhpcyB2ZXJ5IHdlYnNpdGUhKSBhbmQgZXh0cmFjdCBpdCBzb21ld2hlcmUgY29udmVuaWVudC4NCg0KSW4gT3BlblJlZmluZSwgY2xpY2sgYENob29zZSBGaWxlc2AgYW5kIGZpbmQgdGhlIGBsYXJ2YWwgYWJ1bmRhbmNlLmNzdmAgd2hpY2ggaXMgaW4gdGhlIGByYXdkYXRhYCBkaXJlY3Rvcnkgb2YgdGhlIHByb2plY3QgZm9sZGVyLiBUaGVuIGNsaWNrIG9uIHRoZSBgQ3JlYXRlIFByb2plY3RgIGJ1dHRvbiAoeW91IG1heSB3YW50IHRvIHJlbmFtZSB0aGUgcHJvamVjdCkuDQoNCiMjIERhdGEgQ2xlYW5pbmcNCllvdSBhcmUgbm93IHdvcmtpbmcgb24gYSBjb3B5IG9mIHRoZSByYXcgZGF0YSBhbmQgY2hhbmdlcyB5b3UgbWFrZSBpbiBPcGVuUmVmaW5lIHdpbGwgbm90ICdicmVhaycgeW91ciBvcmlnaW5hbCByYXcgZGF0YS4gSW4gaGVyZSB5b3UgY2FuIGRvIGFsbCB0aGUgcmVndWxhciAnc3ByZWFkc2hlZXQteScgdGhpbmdzLiBZb3UgY2FuIGVkaXQgc3BlY2lmaWMgY2VsbHMsIHNvcnQsIHVuZG8vcmVkbywgdmlldyBzdWJzZXRzIG9mIHlvdXIgZGF0YSAoZmFjZXQpLCBldGMuIEJ1dCB0aGUgbW9yZSBwb3dlcmZ1bCBmdW5jdGlvbnMgb2YgT3BlblJlZmluZSBhcmU6IA0KDQotICoqQ2x1c3RlcioqIChjbGljayBvbiBjb2x1bW4gaGVhZGVyIGFycm93LCB0aGVuIGBFZGl0IGNlbGxzID4gQ2x1c3RlciBhbmQgRWRpdC4uLmApIHdoaWNoIG1lYW5zIOKAnGZpbmRpbmcgZ3JvdXBzIG9mIGRpZmZlcmVudCB2YWx1ZXMgdGhhdCBtaWdodCBiZSBhbHRlcm5hdGl2ZSByZXByZXNlbnRhdGlvbnMgb2YgdGhlIHNhbWUgdGhpbmfigJ0uIEZvciBleGFtcGxlLCB0aGUgdHdvIHN0cmluZ3Mg4oCcTmV3IFlvcmvigJ0gYW5kIOKAnG5ldyB5b3Jr4oCdIGFyZSB2ZXJ5IGxpa2VseSB0byByZWZlciB0byB0aGUgc2FtZSBjb25jZXB0IGFuZCBqdXN0IGhhdmUgY2FwaXRhbGl6YXRpb24gZGlmZmVyZW5jZXMuIA0KDQotICoqV2hpdGVzcGFjZSBtYW5hZ2VtZW50KiogKGNsaWNrIG9uIGNvbHVtbiBoZWFkZXIgYXJyb3csIHRoZW4gYEVkaXQgY2VsbHMgPiBDb21tb24gdHJhbnNmb3JtcyA+IFRyaW0gbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS5gIGFuZCBgRWRpdCBjZWxscyA+IENvbW1vbiB0cmFuc2Zvcm1zID4gQ29sbGFwc2UgY29uc2VjdXRpdmUgd2hpdGVzcGFjZS5gKSBTdHJpbmdzIHdpdGggc3BhY2VzIGF0IHRoZSBiZWdpbm5pbmcgb3IgZW5kIGFyZSBwYXJ0aWN1bGFybHkgaGFyZCBmb3Igd2UgaHVtYW5zIHRvIHRlbGwgZnJvbSBzdHJpbmdzIHdpdGhvdXQsIGJ1dCB0aGUgYmxhbmsgY2hhcmFjdGVycyB3aWxsIG1ha2UgYSBkaWZmZXJlbmNlIHRvIHRoZSBjb21wdXRlci4gV2UgdXN1YWxseSB3YW50IHRvIHJlbW92ZSB0aGVzZS4gDQoNCiFbXShwaWN0dXJlcy9PUndoaXRlc3BhY2UucG5nKQ0KDQojIyBSZXByb2R1Y2liaWxpdHkNCg0KT3BlblJlZmluZSBzYXZlcyBldmVyeSBjaGFuZ2UsIGV2ZXJ5IGVkaXQgeW91IG1ha2UgdG8gdGhlIGRhdGFzZXQgaW4gYSBmaWxlIHlvdSBjYW4gc2F2ZSBvbiB5b3VyIG1hY2hpbmUuIElmIHlvdSBoYWQgMjAgZmlsZXMgdG8gY2xlYW4sIGFuZCB0aGV5IGFsbCBoYWQgdGhlIHNhbWUgdHlwZSBvZiBlcnJvcnMsIGFuZCBhbGwgZmlsZXMgaGFkIHRoZSBzYW1lIGNvbHVtbnMsIHlvdSBjb3VsZCBzYXZlIHRoZSBzY3JpcHQsIG9wZW4gYSBuZXcgZmlsZSB0byBjbGVhbiwgcGFzdGUgaW4gdGhlIHNjcmlwdCBhbmQgcnVuIGl0LiBWb2lsYSwgY2xlYW4gZGF0YS4NCg0KLSBJbiB0aGUgYFVuZG8gLyBSZWRvIHNlY3Rpb25gLCBjbGljayBgRXh0cmFjdGAsIHNhdmUgdGhlIGJpdHMgZGVzaXJlZCB1c2luZyB0aGUgY2hlY2sgYm94ZXMuDQotIENvcHkgdGhlIGNvZGUgYW5kIHBhc3RlIGl0IGludG8gYSB0ZXh0IGVkaXRvci4gU2F2ZSBpdCBhcyBhIGAudHh0YCBmaWxlLg0KLSBUbyBydW4gdGhlc2Ugc3RlcHMgb24gYSBuZXcgZGF0YXNldCwgaW1wb3J0IHRoZSBuZXcgZGF0YXNldCBpbnRvIE9wZW5SZWZpbmUsIG9wZW4gdGhlIGBFeHRyYWN0IC8gQXBwbHlgIHNlY3Rpb24sIHBhc3RlIGluIHRoZSBgLnR4dGAgZmlsZSwgY2xpY2sgYEFwcGx5YC4NCg0KRm9yIG1vcmUgaW5mb3JtYXRpb24gYW5kIHR1dG9yaWFscyBvbiBPcGVuUmVmaW5lLCBwbGVhc2Ugc2VlIFtEYXRhIENhcnBlbnRyeV0oaHR0cDovL3d3dy5kYXRhY2FycGVudHJ5Lm9yZy9PcGVuUmVmaW5lLWVjb2xvZ3ktbGVzc29uLykNCg0KIyBSIGFuZCBSU3R1ZGlvDQoNCllvdSBjYW4gZG8gZXZlcnl0aGluZyBtZW50aW9uZWQgYWJvdmUgaW4gW1JdKGh0dHA6Ly9jcmFuLnV0c3RhdC51dG9yb250by5jYS8pLCBpdCBtYXkgYXQgZmlyc3QgYXBwZWFyIG1vcmUgZGlmZmljdWx0IHRvIGRvIGl0IGluIFIsIGJ1dCBpbiBteSBvcGluaW9uLCB5b3Ugd2lsbCBzYXZlIHRpbWUgYnkgc3RyZWFtbGluaW5nIHlvdXIgd29ya2Zsb3cgdXNpbmcganVzdCBvbmUgdG9vbC4gSSdtIG5vdCBhdCBhbGwgZGlzb3VyYWdpbmcgdGhlIHVzZSBvZiBPcGVuUmVmaW5lLGl0IGlzIG9wZW4gc291cmNlIGFuZCByZXByb2R1Y2libGUsIHN1Y2ggbXVjaCBrdWRvcyBpcyBkdWUuDQoNCkZvciB0aGlzIGFuZCBmdXR1cmUgbGVzc29ucywgd2Ugd2lsbCBmb2N1cyBvbiBhY2hpZXZpbmcgcmVwcm9kdWNpYmlsaXR5IGJ5IHVzaW5nIFtSXShodHRwOi8vY3Jhbi51dHN0YXQudXRvcm9udG8uY2EvKSB3aGljaCBpcyBhbiBvcGVuIHNvdXJjZSBsYW5ndWFnZSBhbmQgZW52aXJvbm1lbnQgZm9yIHN0YXRpc3RpY2FsIGNvbXB1dGluZyBhbmQgZ3JhcGhpY3MuIFRoZXJlIGFyZSBtYW55IG90aGVyIHN1Y2ggbGFuZ3VhZ2VzIChlLmcuIFtQeXRob25dKGh0dHBzOi8vd3d3LnB5dGhvbi5vcmcvKSwgW0p1bGlhXShodHRwczovL2p1bGlhbGFuZy5vcmcvKSwgW01BVExBQl0oaHR0cHM6Ly93d3cubWF0aHdvcmtzLmNvbS9wcm9kdWN0cy9tYXRsYWIuaHRtbCksZXRjKSB1c2VkIGJ5IGNvbnNlcnZhdGlvbmlzdHMsIGJpb2xvZ2lzdCwgYW5kIG9jZWFub2dyYXBoZXJzOyBob3dldmVyLCB3ZSBiZWxpZXZlIFIgaXMgY3VycmVudGx5IHRoZSBtb3N0IHdpZGVseSBhZG9wdGVkIGFtb25nIG91ciBjb2xsZWFndWVzIGFuZCBhbHNvIGhhcyB0aGUgbW9zdCBjb252ZW5pZW50IHNldCBvZiBzdGF0aXN0aWNhbCB0b29scyBkZXZlbG9wZWQgZm9yIG91ciBmaWVsZC4NCg0KIyMgSW50cm8gdG8gUg0KDQpJbiAidGhlIG9sZGVuIGRheXMiIHdlIH5+aGFkIHRvIHdhbGsgdG8gc2Nob29sIHVwaGlsbCBib3RoIHdheXN+fiB1c2VkIFIgaW4gdGhlIHRlcm1pbmFsIG9yIHVzaW5nIHRoZSBidWlsdCBpbiBncmFwaGljYWwgdXNlIGludGVyZmFjZSAoR1VJKS4gWWVzLCBiZWZvcmUgMjAxMSwgUlN0dWRpbyBkaWQgbm90IGV4aXN0IGFuZCB5ZXMsIFIgYW5kIFJTdHVkaW8gYXJlIG5vdCB0aGUgc2FtZSB0aGluZyENCg0KUiBpcyBhY2Nlc3NpYmxlIGluIHRoZSB0ZXJtaW5hbCAodGhhdCB0aGluZyB0aGF0IGxvb2tzIGxpa2UgRE9TLCBhbmQgaW4gY2FzZSBteSAnb2xkJyBpcyBzaG93aW5nLCB0aGUgdGhpbmcgdGhhdCBpcyB1c3VhbGx5IGEgYmxhY2sgc2NyZWVuLCBhIGJsaW5raW5nIGN1cnNvciBhbmQgeW91IGNhbiBvbmx5IHR5cGUgaW4gY29tbWFuZHMpIGJ5IHR5cGluZyBgUi5leGVgIG9uIFdpbmRvd3MsIG9yIGp1c3QgYFJgIG9uIE1hYyBvciBMaW51eC4gSW4gdGhpcyB3YXksIHlvdSBjYW4gdHlwZSBjb21tYW5kcyBpbiBvbmUgYnkgb25lLCBvciBzaW1pbGFyIHRvIHdoYXQgd2UganVzdCBzYXcgd2l0aCBPcGVuUmVmaW5lLCB5b3Ugc2F2ZSB5b3VyIHN0ZXBzL2luc3R1Y3Rpb25zL2NvbW1hbmRzIGluIGEgcGxhaW4gdGV4dCBmaWxlICh3aXRoIGEgYC5SYCBleHRlbnNpb24gaW5zdGVhZCBvZiBgLnR4dGApIGFuZCB5b3UgY2FuIHJ1biB0aG9zZSBpbiB0aGUgdGVybWluYWwgYnkgdHlwaW5nIGBSc2NyaXB0LmV4ZSBzY3JpcHRuYW1lLlJgIG9uIFdpbmRvd3MsIG9yIGp1c3QgYFJzY3JpcHQgc2NyaXB0bmFtZS5SYCBvbiBNYWMgb3IgTGludXguIFdoaWxlIEkgZG9uJ3Qgb2Z0ZW4gd29yayBpbiB0aGlzIHdheSBhbnltb3JlLCBidXQgdGhpcyBpcyB0aGUgb25seSBvcHRpb24gd2hlbiB1c2luZyBbQ29tcHV0ZSBDYW5hZGFdKGh0dHBzOi8vd3d3LmNvbXB1dGVjYW5hZGEuY2EvcmVzZWFyY2gtcG9ydGFsL25hdGlvbmFsLXNlcnZpY2VzL2NvbXB1dGUvKSdzIGF3ZXNvbWUgcmVzb3VyY2VzLiBVc2luZyB0aGUgY29tbWFuZHMgYWJvdmUgYW5kIGEgbGl0dGxlIHNlcnZlciBzcGVjaWZpYyBtYWdpYywgeW91IGNhbiBydW4geW91ciBzY3JpcHRzIG9uIDEwMCdzIG9mIHByb2Nlc3NvcnMgaW5zdGVhZCBvZiB0aGUgb25lIGxvbmVseSBwcm9jZXNzb3Igb24geW91ciBjb21wdXRlciEgSSd2ZSB1c2VkIGh1bmRyZWRzIG9mIHllYXJzIG9mIGNvbXB1dGVyIHRpbWUgaW4gYSBtYXR0ZXIgb2Ygd2Vla3MsIGFsbCBmb3IgZnJlZSEgSWYgeW91IGFyZSBhZmZpbGlhdGVkIHdpdGggYW55IENhbmFkaWFuIHVuaXZlcnNpdHksIHlvdSBjYW4gZG8gdGhpcyB0b28hDQoNCiFbIlRoaXMgaXMgd2hhdCAncGxhaW4gdmFuaWxsYScgUiBsb29rcyBsaWtlIl0ocGljdHVyZXMvUnRlcm0ucG5nKQ0KDQpSIGFsc28gY29tZSB3aXRoIGl0J3Mgb3duIEdVSSwgaW4gd2hpY2ggeW91IGNhbiBoYXZlIGEgc2NyaXB0IGVkaXRvciwgd2hpY2ggaXMgZXNzZW50aWFsbHkgYSBwbGFpbiB0ZXh0IGVkaXRvciwgdG8gd3JpdGUvZGV2ZWxvcCB5b3VyIHNjcmlwdCBhbmQgYW4gaW50ZXJhY3RpdmUgUiBjb25zb2xlIHdoZXJlIHlvdSBjYW4gYWN0dWFsbHkgZXhlY3V0ZSBjb21tYW5kcy4gVGhlIGFkdmFudGFnZSBvZiB0aGUgR1VJIGlzIHRoYXQgeW91IGNhbiBleGVjdXRlIHRoZSBlbnRpcmUgc2NyaXB0ICgnc291cmNlJykgb3IgcnVuIGl0IGxpbmUgYnkgbGluZSBhbGwgd2hpbGUgcmVjb3JkaW5nIHlvdXIgY29tbWFuZHMgaW4gdGhlIHNjcmlwdCBmaWxlLg0KDQohW10ocGljdHVyZXMvUmd1aS5wbmcpDQoNCiMjIEludHJvIHRvIFJTdHVkaW8NCg0KW1JTdHVkaW9dKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL1JTdHVkaW8vI0Rlc2t0b3ApIHRha2VzIHRoaXMgR1VJIGNvbmNlcHQgYSBiaXQgZnVydGhlciBhbmQgcHJvdmlkZXMgeW91IHdpdGggc2V2ZXJhbCBleHRyYSBzdXBwb3J0IHdpbmRvdy4gSWYgdGhlIGlkZWEgb2YgaGF2aW5nIHdpbmRvd3MgZm9yIHlvdXIgZW52aXJvbm1lbnQsIHlvdXIgZmlsZXMsIHlvdXIgcGxvdHMsIGFzIHdlbGwgYXMgcGFja2FnZXMgYW5kIGEgaGVscCB0YWIgYWxsIGF0IGhhbmQgZG9lcyBub3QgZXhjaXRlIHlvdSwgaG9sZCBvbiB0aWdodCwgeW91J2xsIGdldCB0aGVyZS4NCg0KIVtdKHBpY3R1cmVzL1JTdHVkaW8ucG5nKQ0KDQpUaGVyZSdzIGFsc28gYSBsb3QgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBSU3R1ZGlvIG9uIHRoZWlyIFtjaGVhdHNoZWV0XShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNi8wMS9yc3R1ZGlvLUlERS1jaGVhdHNoZWV0LnBkZikuIE9uIHRoZSBzdWJqZWN0IG9mIGNoZWF0c2hlZXRzLCBSU3R1ZGlvIGhhcyBkZXZlbG9wZWQgc2V2ZXJhbCAqKnN1cGVyIHVzZWZ1bCoqIFtjaGVhdHNoZWV0c10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLyk7IHNlcmlvdXNseSwgeW91IHByb2JhYmx5IHdpbGwgd2FudCB0byBwcmludCBtb3N0IG9mIHRoZXNlIGFuZCBwdXQgdGhlbSBvbiB0aGUgd2FsbCBpbiB5b3VyIG9mZmljZS4NCg0KIyMgRW5vdWdoIHRhbGshIExldCdzIGdldCBjb2RpbmchIFRoZSBGdW5kYW1lbnRhbHMNCllvdSByZWFkIG15IG1pbmQhIEhvd2V2ZXIsIGJlZm9yZSB3ZSBnZXQgdG8gY2xlYW5pbmcgdGhlIGRhdGEsIHdlIG5lZWQgdG8gY292ZXIgYSBmZXcgUiBmdW5kYW1lbnRhbHMgc28gdGhhdCB3aGF0IHdlIGRvIGluIGxhdGVyIHN0ZXBzIG1ha2VzIHNlbnNlLg0KDQpHbyBiYWNrIHRvIHRoZSBwcm9qZWN0IGZvbGRlciB5b3UgZG93bmxvYWRlZCBkdXJpbmcgdGhlIFtPcGVuUmVmaW5lIGxlc3Nvbl0oZmlsZTovLy9DOi9Vc2Vycy9SZW1pLVdvcmsvRGVza3RvcC8yMDE2LTA1LTAxLUNIT05lLURhdGEtQ2FycC9jbGVhbmluZy5odG1sI29wZW5yZWZpbmUpIGFuZCBvcGVuIHRoZSBgMjAxNy1DSE9OZS1EYXRhLlJwcm9qYCBmaWxlLiBUaGlzIGlzIGFuIFIgcHJvamVjdCBmaWxlIHRoYXQgYWxsb3dzIHlvdSB0byBzZXQgYSBudW1iZXIgb2Ygb3B0aW9ucyBmb3IgdGhlIHByb2plY3QgKHNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykpLCBidXQgZm9yIG91ciBwdXJwb3NlcyBqdXN0IGtub3cgdGhhdCB0aGUgcHJvamVjdCBmaWxlIGlzIHNldHRpbmcgdGhlICd3b3JraW5nIGRpcmVjdG9yeScsIGl0IHRlbGxzIFIgd2hlcmUgYWxsIHlvdXIgZmlsZXMgYXJlLiBXZSdsbCBnZXQgYmFjayB0byB0aGF0IGxhdGVyLg0KDQpJbiBSIHlvdSBjYW4gZG8gbWF0aCwgdHlwZSB0aGUgY29tbWFuZCBiZWxvdyBpbiB5b3VyIFIgY29uc29sZSBhbmQgaGl0IGVudGVyOg0KYGBge3J9DQoxKzENCmBgYA0KDQpZb3UgY2FuIGFsc28gYXNzaWduIHZhbHVlcyB0byB2YXJpYWJsZXMsIG9yIGluIFIgcGFybGFuY2UgYW4gJ29iamVjdCcgdXNpbmcgdGhlIGA8LWAgc3ltYm9sIChzaG9ydGtleSBgQWx0YCArIGAtYCkuDQpgYGB7cn0NCmEgPC0gMg0KYiA8LSAxKzINCmBgYA0KDQpZb3UnbGwgbm90aWNlIHRoYXQgdGhlcmUgd2FzIG5vIG91dHB1dCB0aGlzIHRpbWUgdGhlcmUgd2FzIG5vIG91dHB1dC4gVGhhdCdzIGJlY2F1c2UgdGhlIHZhbHVlIG9uIHRoZSByaWdodCBzaWRlIG9mIHRoZSBgPC1gIHN5bWJvbCBpcyBhc3NpZ25lZCB0byBhIG9iamVjdCwgaXQgZ29lcyB0byB5b3VyIGVudmlyb25tZW50ICh0b3AgcmlnaHQgd2luZG93KSBpbnN0ZWFkIG9mIGJlaW5nIG91dHB1dCB0byB0aGUgY29uc29sZS4gSW4gdGhlIDErMSBleGFtcGxlIGFib3ZlLCB0aGVyZSB3YXMgbm8gb2JqZWN0IHRvIGdvIHRvLCBzbyBpdCBkZWZhdWx0ZWQgdG8gcHJpbnRpbmcgaW4gdGhlIGNvbnNvbGUuDQoNCllvdSBjYW4gc2VlIHRoZSBjb250ZW50cyBvZiBhIG9iamVjdCBpbiB0aGUgZW52aXJvbm1lbnQgd2luZG93LCBvciBieSB0eXBpbmcgdGhlIG9iamVjdCBpbnRvIHRoZSBjb25zb2xlLiBZb3UgY2FuIGFsc28gdXNlIHRoZXNlIG9iamVjdHMgbGlrZSBhbGdlYnJhDQpgYGB7cn0NCmENCmINCmEvYg0KYGBgDQoNClVwIHRvIG5vdywgd2UndmUgYmVlbiBkZWFsaW5nIHdpdGggbnVtYmVycywgYnV0IFIgY2FuIGFsc28gZGVhbCB3aXRoIGNoYXJhY3RlciBzdHJpbmcgaWYgc3Vycm91bmRlZCBieSBzaW5nbGUgb3IgZG91YmxlIHF1b3RhdGlvbiBtYXJrcy4gQWNjb3JkaW5nIHRvIFIgaGVscCAoSSBsZWFybmVkIHRoaXMgdG9kYXkhKTogIlNpbmdsZSBhbmQgZG91YmxlIHF1b3RlcyBkZWxpbWl0IGNoYXJhY3RlciBjb25zdGFudHMuIFRoZXkgY2FuIGJlIHVzZWQgaW50ZXJjaGFuZ2VhYmx5IGJ1dCBkb3VibGUgcXVvdGVzIGFyZSBwcmVmZXJyZWQgKGFuZCBjaGFyYWN0ZXIgY29uc3RhbnRzIGFyZSBwcmludGVkIHVzaW5nIGRvdWJsZSBxdW90ZXMpLCBzbyBzaW5nbGUgcXVvdGVzIGFyZSBub3JtYWxseSBvbmx5IHVzZWQgdG8gZGVsaW1pdCBjaGFyYWN0ZXIgY29uc3RhbnRzIGNvbnRhaW5pbmcgZG91YmxlIHF1b3Rlcy4iDQoNCmBgYHtyfQ0KZiA8LSAiVGhpcyBpcyBhIGNoYXJhY3RlciBzdHJpbmcsIHlvdSBjYW4gdGVsbCBiZWNhdXNlIG9mIHRoZSBxdW90YXRpb24gbWFya3MiDQpgYGANCg0KQSBvYmplY3QgY2FuIGFsc28gY29udGFpbiBtdWx0aXBsZSB2YWx1ZXMuIFRoZSBgOmAgc3ltYm9sIGVzc2VudGlhbGx5IG1lYW5zICd0bycNCg0KYGBge3J9DQp4IDwtIDE6Mw0KYGBgDQoNCkFub3RoZXIgd2F5IHRvIGRvIHRoYXQsIHdpdGggbW9yZSBmbGV4aWJpbGl0eSBpcyB1c2luZyB0aGUgYGMoKWA7IHRoZSBjIGlzIHNob3J0IGZvciBjb25jYXRlbmF0ZSBhbmQgdGhlIHJvdW5kIGJyYWNrZXRzIGluZGljYXRlIHRoYXQgaXQncyBhIGZ1bmN0aW9uLiBTbyB0aGlzIGNvbmNhdGVuYXRlIGZ1bmN0aW9uIHdpbGwgY29uY2F0ZW5hdGUgYWxsIHRoZSAnYXJndW1lbnRzJyAodGhpbmdzIGluc2lkZSB0aGUgcm91bmQgYnJhY2tldHMpIHdoaWNoIGFyZSBzZXBhcmF0ZWQgYnkgY29tbWFzLiBZb3UgY2FuIGFsc28gY29tYmluZSB0aGVzZSBzdHJhdGVnaWVzDQoNCmBgYHtyfQ0KeCA8LSBjKDEsMyw1KQ0KeSA8LSBjKDE6NCw2LDgpDQpgYGANCg0KVGhlcmUgYXJlIG1hbnkgZnVuY3Rpb25zLCBidXQgdGhleSBhbGwgZm9sbG93IHRoZSBmb3JtYXQgYGZ1bmN0aW9uTmFtZShhcmd1bWVudDEsYXJndW1lbnQyLGFyZ3VtZW50MywuLi4pYCB3aGVyZSB0aGUgJ2FyZ3VtZW50cycgYXJlIHRoZSBpbnB1dCB0byB0aGUgZnVuY3Rpb24uIFNvbWUgYXJlIGZhaXJseSBzdHJhaWdodGZvcndhcmQ6DQoNCmBgYHtyfQ0KbWVhbih5KQ0KYGBgDQpCdXQgZXZlbiB0aGVuIHRoZXJlIGFyZSBzb21lIHN1cnByaXNlcywgbGV0J3MgbG9vayBhdCB0aGUgaGVscCBmaWxlIGZvciBgbWVhbigpYC4gVG8gZG8gdGhhdCB5b3UgY2FuOg0KLSBpZiB5b3UgYXJlIG9uIHRoZSBhY3RpdmUgbGluZSBpbiB0aGUgY29uc29sZSBvciBhbnl3aGVyZSBpbiBhIHNjcmlwdCwgcHV0IHlvdXIgY3Vyc29yIG9uIHRoZSBmdW5jdGlvbiBhbmQgcHJlc3MgYEYxYA0KLSBpbiB0aGUgY29uc29sZSwgdHlwZSBgP21lYW5gIChvciBgPz9tZWFuYCBpZiB5b3VyIG5vdCBzbyBzdXJlIGBtZWFuYCBpcyB0aGUgbmFtZSBvZiB0aGUgZnVuY3Rpb24pDQotIGZpbmQgdGhlIGhlbHAgd2luZG93IChvbmUgb2YgdGhlIHRhYnMgZm9yIHRoZSBib3R0b20gcmlnaHQgd2luZG93KSBhbmQgdXNlIHRoZSBzZWFyY2ggYmFyDQotIGFsc28sIHdoZW4gYWxsIGVsc2UgZmFpbHMsIEdvb2dsZSBpcyB5b3VyIGZyaWVuZCENCg0KQW55IG1ldGhvZCBzaG91bGQgZ2V0IHlvdSB0byBzb21ldGhpbmcgbGlrZSB0aGlzOg0KIVtdKHBpY3R1cmVzL2hlbHAucG5nKQ0KSW4gUiBpbiBtb3N0IGNhc2VzIHlvdSBjb3VsZCB1c2UgYD1gIGluc3RlYWQgb2YgdGhlIGA8LWAgc3ltYm9sIHdpdGggbm8gcHJvYmxlbXMgd2hlbiB5b3UgYXJlIGFzc2lnbmluZyBhIHZhbHVlIHRvIGEgb2JqZWN0LiBIb3dldmVyLCBpdCBpcyBiZXN0IHByYWN0aWNlIHRvIHVzZSBgPC1gIHdoZW4gYXNzaWduaW5nIGVudmlyb25tZW50IG9iamVjdHMgYW5kIGA9YCB3aGVuIGRlZmluaW5nIGZ1bmN0aW9uIGFyZ3VtZW50cy4gT2gsIGFuZCBgTkFgIGluIFIgbWVhbnMg4oCYTm90IEF2YWlsYWJsZeKAmSAvIE1pc3NpbmcgVmFsdWVzLiBMaWtlIHNvOg0KYGBge3J9DQp4IDwtIGMoMSwyLDUsNyw4OCwzLDQsMiw0LDYsNyxOQSkNCg0KbWVhbih4KQ0KDQptZWFuKHgsIG5hLnJtID0gVFJVRSkNCmBgYA0KDQpJIGFsc28gc251Y2sgYSBgVFJVRWAgaW4gdGhlcmU7IGBUUlVFYCBhbmQgYEZBTFNFYCBhcmUgY2FsbGVkIGxvZ2ljYWwgYW5kIGFyZSBkaXN0aW5jdCBmcm9tIG51bWVyaWMgb3IgY2hhcmFjdGVyIHN0cmluZ3MuIFRoZXkgYXJlIHNvbWV0aW1lcyB1c2VkIGFzIGFyZ3VtZW50cyB2YWx1ZXMsIGJ1dCB0aGV5IGNhbiBhbHNvIHVzZWQgdG8gdGVzdCB0aGluZ3MuIFRoZSBgPT1gIGFza3MgaWYgYm90aCBzaWRlcyBhcmUgZXF1YWwgKHNpbmNlIHRoZSBzaW5nbGUgYD1gIGlzIGFscmVhZHkgdXNlZCBmb3Igb3RoZXIgdGhpbmdzKSwgYW5kIHRoZSBgIT1gIGFza3MgaWYgYm90aCBzaWRlcyBhcmUgbm90IGVxdWFsLg0KDQpgYGB7cn0NCjI9PTENCjI9PTINCjIhPTENCmBgYA0KIyMgQ3JlYXRpbmcgeW91ciBvd24gc2NyaXB0cw0KVXAgdW50aWwgbm93LCB3ZSd2ZSBiZWVuIHBsYXlpbmcgaW4gdGhlIGNvbnNvbGUgd2hpY2ggbWVhbnMgdGhlICdpbnN0cnVjdGlvbnMnIHdlIG5lZWQgdG8gc2F2ZSB0byByZXByb2R1Y2Ugb3VyIHNjaWVuY2UgYXJlIGxvc3QgKHdlbGwgbm90IHJlYWxseSwgdGhleSBjYW4gYmUgcmV0cmlldmVkIGZyb20gdGhlIEhpc3RvcnkgdGFiIGluIHRoZSB0b3AgcmlnaHQsIG9yIHRoZSBjb25zb2xlIGlmIGl0IGhhc24ndCByb2xsZWQgb2ZmIHRoZSBzY3JlZW4pLiBJdCBpcyBhIGdvb2QgaWRlYSB0byBkZXZlbG9wIHlvdXIgYW5hbHlzaXMgdXNpbmcgYSBzY3JpcHQgZmlsZSAodGhvc2Ugc2ltcGxlIHRleHQgZmlsZXMgd2l0aCB0aGUgYC5SYCBleHRlbnNpb24gSSB3YXMgdGFsa2luZyBhYm91dCBlYXJsaWVyKSBiZWNhdXNlIHlvdSBjYW4gc2F2ZSB5b3VyIGNvZGUgZWFzaWx5Lg0KDQpUbyBjcmVhdGUgYSBuZXcgc2NyaXB0LCB5b3VyIGNhbiBjbGljayBvbiB0aGUgbGl0dGxlIHBhcGVyIHdpdGggdGhlIHBsdXMgc3ltYm9sIChzZWUgYmVsb3cpLCBvciB5b3UgY2FuIGhpdCBgQ3RybGArYFNoaWZ0YCtgTmAgKFdpbmRvd3MpLCBvciBgQ29tbWFuZGArYFNoaWZ0YCtgTmAgKE1hYyksIGFuZCBpZiB0aGF0J3Mgbm90IGVub3VnaCBvcHRpb25zLCB5b3UgY2FuIGNsaWNrIGBGaWxlID4gTmV3IEZpbGUgPiBOZXcgU2NyaXB0YA0KDQohW10ocGljdHVyZXMvbmV3c2NyaXB0LnBuZykNCg0KVGhlc2Ugc2NyaXB0cyBhcmUgZGVzaWduZWQgdG8gcmVhZCBieSBSIGZyb20gdG9wIHRvIGJvdHRvbSB3aGVuIHlvdSBoaXQgdGhlICFbXShwaWN0dXJlcy9zb3VyY2UucG5nKSBidXR0b24sIG9yIGBDdHJsYCtgU2hpZnRgK2BTYCAoV2luZG93cyksIG9yIGBDb21tYW5kYCtgU2hpZnRgK2BTYCAoTWFjKS4gQWx0ZXJuYXRpdmVseSwgeW91IGNhbiBydW4gcG9ydGlvbnMgb2YgeW91ciBjb2RlIHdpdGggYEN0cmxgK2BFbnRlcmAgKFdpbmRvd3MpLCBvciBgQ29tbWFuZGArYEVudGVyYCAoTWFjKSBhbmQgZWl0aGVyIHB1dHRpbmcgeW91ciBjdXJzb3Igb24gYSBsaW5lIHRvIHJ1biB0aGUgZW50aXJlIGxpbmUsIG9yIGhpZ2hsaWdodGluZyBhIHN1YnNlY3Rpb24gb2YgY29kZSB0byBydW4ganVzdCB0aGF0IHBvcnRpb24uDQoNCj4qKlByby10aXAqKg0KPklmIHlvdSBkb24ndCB3YW50IFIgdG8gcmVhZCBzb21ldGhpbmcsIHVzIHRoZSBgI2AuIEFueXRoaW5nIHRoYXQgaXMgcHJlY2VkZWQgYnkgYSBgI2AgaXMgcmVnYXJkZWQgYXMgYSAnY29tbWVudCcgYnkgUiBhbmQgaXQgZG9lcyBub3QgdHJ5IHRvIGV4ZWN1dGUgdGhvc2UgbGluZXMgKGkuZS4gUiBpZ25vcmVzIGFueXRoaW5nIGFmdGVyIGEgYCNgKS4gDQo+IFRoaXMgaXMgYWxzbyB1c2VmdWwgaWYgeW91IHdhbnQgdG8gYXZvaWQgcnVubmluZyBhIGZldyBsaW5lcyBvZiBjb2RlIHdoZW4geW91IGFyZSBkZXZlbG9waW5nIHlvdXIgc2NyaXB0LiBJbnN0ZWFkIG9mIHR5cGluZyBhIGAjYCBpbiBmcm9udCBvZiBlYWNoIGxpbmUgb2YgY29kZSwgeW91IGNhbiBoaWdobGlnaHQgdGhlIGxpbmVzIHlvdSB3YW50IGNvbW1lbnRlZCBvdXQgYW5kIGhpdCBgQ3RybGArYFNoaWZ0YCtgQ2AgKFdpbmRvd3MpLCBvciBgQ29tbWFuZGArYFNoaWZ0YCtgQ2AgKE1hYykuIE1hZ2ljIQ0KDQpDb21tZW50aW5nIGlzIHN1cGVyIHVzZWZ1bCB0byBpbmNsdWRlIGh1bWFuIHJlYWRhYmxlIGluc3RydWN0aW9ucy9kb2N1bWVudGF0aW9uIGluIHlvdXIgY29kZS4gTGV0J3MgZ2l2ZSB0aGlzIGEgdHJ5LCB3cml0ZSB0aGlzIGNodW5rIG9mIGNvZGUgaW50byB5b3VyIHNjcmlwdCwgdGhlbiBydW4gaXQgbGluZSBieSBsaW5lLg0KYGBge3J9DQp4IDwtIDENCg0KeCA8LSAyDQoNCiMgeCA8LSAzDQpgYGANCldoYXQgaXMgdGhlIHZhbHVlIG9mIGB4YCBhZnRlciBydW5uaW5nIGFsbCB0aGUgbGluZXMgYW5kIHdoeT8NCg0KIyMgSW5kZXhpbmcgYW5kIGRpbWVuc2lvbnMNCg0KDQoNCiMjIE1ha2luZyB5b3VyIG93biBmdW5jdGlvbnMNCg0KDQojIyBXaGF0IGFyZSAncGFja2FnZXMnPw0KDQoNCiMjIFJlYWRpbmcgaW4gZGF0YQ0KDQoNCg0KIyBIb21ld29yaw0KaW5zdGFsbCBhbGwgdGhlIHBhY2thZ2VzDQoNCg0KDQoNCls8PEJBQ0tdKGh0dHBzOi8vcmVtaS1kYWlnbGUuZ2l0aHViLmlvLzIwMTctQ0hPTmUtRGF0YS8pDQo=